package io.kiki.sba.registry.server.clientInterface.bootstrap;

import io.kiki.sba.registry.server.shared.config.CommonConfig;
import io.kiki.sba.registry.server.shared.env.ServerEnv;
import io.kiki.sba.registry.util.OsUtils;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * The type Session server config bean.
 */
@ConfigurationProperties(prefix = ConfigBean.PREFIX)
public class ConfigBean implements Config {

    /**
     * The constant PREFIX.
     */
    public static final String PREFIX = "session.server";

    @Setter
    private int serverPort = 9600;

    @Setter
    private int syncSessionPort = 9602;

    @Setter
    private int consolePort = 9604;

    @Setter
    private int syncSessionIOLowWaterMark = 1024 * 288;

    @Setter
    private int syncSessionIOHighWaterMark = 1024 * 320;

    @Setter
    private int clientIOLowWaterMark = 1024 * 384;

    @Setter
    private int clientIOHighWaterMark = 1024 * 512;

    @Setter
    private int metaServerPort = 9610;

    @Setter
    private int dataServerPort = 9620;

    @Setter
    private int dataServerNotifyPort = 9623;

    @Setter
    private int httpServerPort = 9603;

    private int schedulerHeartbeatIntervalSecs = 1;

    @Setter
    private int accessDataExecutorPoolSize = OsUtils.getCpuCount() * 4;

    @Setter
    private int accessDataExecutorQueueSize = 10000;

    @Setter
    private int accessSubDataExecutorPoolSize = OsUtils.getCpuCount() * 4;

    @Setter
    private int accessSubDataExecutorQueueSize = 40000;

    @Setter
    private int dataChangeExecutorPoolSize = OsUtils.getCpuCount() * 3;

    @Setter
    private int dataChangeExecutorQueueSize = 20000;

    @Setter
    private int dataChangeFetchTaskWorkerSize = OsUtils.getCpuCount() * 6;

    @Getter
    @Setter
    private int subscriberRegisterTaskWorkerSize = OsUtils.getCpuCount() * 4;


    @Setter
    private int metadataRegisterExecutorPoolSize = OsUtils.getCpuCount() * 3;


    @Setter
    private int metadataRegisterExecutorQueueSize = 1000;


    @Setter
    private int scanExecutorPoolSize = OsUtils.getCpuCount() * 3;


    @Setter
    private int scanExecutorQueueSize = 100;

    @Setter
    private int dataChangeDebouncingMillis = 1000;
    @Setter
    private int dataChangeMaxDebouncingMillis = 3000;

    @Getter
    @Setter
    private int slotSyncMaxBufferSize = 5000;

    @Getter
    @Setter
    private int slotSyncWorkerSize = OsUtils.getCpuCount() * 3;

    @Setter
    private int metaNodeBufferSize = 2000;

    @Setter
    private int metaNodeWorkerSize = OsUtils.getCpuCount() * 4;

    @Setter
    private int accessMetadataMaxBufferSize = 10000;

    @Setter
    private int accessMetadataWorkerSize = OsUtils.getCpuCount() * 4;

    @Setter
    private int consoleExecutorPoolSize = OsUtils.getCpuCount() * 3;

    @Setter
    private int consoleExecutorQueueSize = 2000;

    @Setter
    private int clientNodeExchangeTimeoutMillis = 2000;
    @Setter
    private int clientNodePushConcurrencyLevel = 1;
    @Setter
    private int dataNodeExchangeTimeoutMillis = 3000;

    @Setter
    @Getter
    private int dataNodeExchangeForFetchDatumTimeoutMillis = 5000;

    @Setter
    private int metaNodeExchangeTimeoutMillis = 3000;

    @Setter
    private int pushTaskExecutorPoolSize = OsUtils.getCpuCount() * 3;

    @Setter
    private int pushTaskExecutorQueueSize = pushTaskExecutorPoolSize * 3000;

    @Setter
    private int pushDataTaskRetryFirstDelayMillis = 500;

    @Setter
    private int pushDataTaskRetryIncrementDelayMillis = 500;

    @Setter
    private int pushDataTaskDebouncingMillis = 500;

    @Setter
    private int pushTaskRetryTimes = 3;

    @Setter
    private int dataNodeExecutorWorkerSize = OsUtils.getCpuCount() * 8;

    @Setter
    private int dataNodeExecutorQueueSize = 20000;

    @Setter
    private int dataNodeRetryBackoffMillis = 1000;

    @Setter
    private int dataNodeRetryTimes = 5;

    @Setter
    private int dataNodeRetryQueueSize = 1000;

    @Setter
    private int dataNodeMaxBatchSize = 100;

    @Setter
    private int scanSubscriberIntervalMillis = 1000 * 5;

    @Setter
    @Getter
    private double accessLimitRate = 30000.0;

    private String sessionServerRegion;

    @Setter
    private String sessionServerDataCenter;

    // begin config for enterprise version

    /**
     * forever close push zone，such as:RZBETA
     */
    @Setter
    @Getter
    private String invalidForeverZones = "";
    /**
     * config regex,exception to the rule of forever close push zone
     */
    @Setter
    @Getter
    private String invalidIgnoreDataidRegex = "";

    private volatile Set<String> invalidForeverZonesSet;

    private volatile Optional<Pattern> invalidIgnoreDataIdPattern = null;

    @Setter
    private int dataClientConnNum = 10;
    @Setter
    private int dataNotifyClientConnNum = 5;
    @Setter
    private int sessionSchedulerPoolSize = OsUtils.getCpuCount();

    @Setter
    private int slotSyncPublisherMaxNum = 512;

    @Setter
    private int cacheDigestIntervalMinutes = 15;

    @Setter
    private int cacheCountIntervalSecs = 30;

    @Setter
    private int cacheDatumMaxWeight = 1024 * 1024 * 256;

    @Setter
    private int cacheDatumExpireSecs = 5;

    // metadata config start

    @Getter
    @Setter
    private int heartbeatCacheCheckerInitialDelaySecs = 60;

    @Getter
    @Setter
    private int heartbeatCacheCheckerSecs = 60;

    @Getter
    @Setter
    private int revisionHeartbeatInitialDelayMinutes = 10;

    @Getter
    @Setter
    private int revisionHeartbeatMinutes = 10;

    @Setter
    private int clientManagerIntervalMillis = 1000;

    @Setter
    private int clientOpenIntervalSecs = 5;

    // metadata config end

    // end config for enterprise version

    private CommonConfig commonConfig;

    private volatile Collection<String> metaAddresses;

    @Setter
    private int clientManagerAddressIntervalMillis = 1000;

    @Setter
    private int systemPropertyIntervalMillis = 3000;

    @Setter
    private int pushTaskBufferBucketSize = 4;

    @Setter
    private int pushCircuitBreakerThreshold = 10;


    @Setter
    private int pushCircuitBreakerSilenceMillis = 60 * 1000;


    @Setter
    private int pushAddressCircuitBreakerThreshold = 50;


    @Setter
    private int pushConsecutiveSuccess = 1;

    @Getter
    @Setter
    private int skipPushEmptySilentMillis = 30 * 1000;

    @Setter
    private int scanWatcherIntervalMillis = 1000 * 3;

    @Setter
    private int watchConfigFetchBatchSize = 100;
    @Setter
    private int watchConfigFetchIntervalMillis = 1000 * 30;
    @Setter
    private int watchConfigFetchLeaseSecs = 30;
    @Setter
    private boolean watchConfigEnable = false;

    @Setter
    private int watchPushTaskWorkerSize = OsUtils.getCpuCount() * 2;

    @Setter
    private int watchPushTaskMaxBufferSize = 10000;

    @Setter
    private boolean gracefulShutdown = false;

    @Setter
    private int scanTimeoutMills = 10 * 1000;


    public ConfigBean(CommonConfig commonConfig) {
        this.commonConfig = commonConfig;
    }


    @Override
    public int getServerPort() {
        return serverPort;
    }


    @Override
    public int getSyncSessionPort() {
        return syncSessionPort;
    }


    @Override
    public int getSchedulerHeartbeatIntervalSecs() {
        return schedulerHeartbeatIntervalSecs;
    }


    public void setSchedulerHeartbeatTimeout(int schedulerHeartbeatIntervalSec) {
        this.schedulerHeartbeatIntervalSecs = schedulerHeartbeatIntervalSec;
    }

    @Override
    public int getPushTaskRetryTimes() {
        return pushTaskRetryTimes;
    }


    @Override
    public int getClientNodeExchangeTimeoutMillis() {
        return clientNodeExchangeTimeoutMillis;
    }


    @Override
    public int getDataNodeExchangeTimeoutMillis() {
        return dataNodeExchangeTimeoutMillis;
    }


    @Override
    public int getMetaServerPort() {
        return metaServerPort;
    }


    @Override
    public String getSessionServerRegion() {
        if (commonConfig != null) {
            String region = commonConfig.getRegion();
            if (region != null && !region.isEmpty()) {
                return commonConfig.getRegion().toUpperCase();
            }
        }

        if (sessionServerRegion != null) {
            sessionServerRegion = sessionServerRegion.toUpperCase();
        }
        return sessionServerRegion;
    }


    public void setSessionServerRegion(String sessionServerRegion) {
        if (sessionServerRegion != null) {
            sessionServerRegion = sessionServerRegion.toUpperCase();
        }
        this.sessionServerRegion = sessionServerRegion;
    }

    @Override
    public Set<String> getLocalDataCenterZones() {
        return commonConfig.getLocalSegmentRegions();
    }

    @Override
    public String getClientCell(String subscriberCell) {
        return this.getSessionServerRegion();
    }

    @Override
    public boolean isLocalDataCenter(String dataCenter) {
        return StringUtils.equals(getDataCenter(), dataCenter);
    }

    @Override
    public String getDataCenter() {
        if (commonConfig != null) {
            String dataCenter = commonConfig.getDataCenter();
            if (dataCenter != null && !dataCenter.isEmpty()) {
                return commonConfig.getDataCenter();
            }
        }

        return sessionServerDataCenter;
    }


    @Override
    public int getMetaNodeExchangeTimeoutMillis() {
        return metaNodeExchangeTimeoutMillis;
    }


    @Override
    public int getDataServerPort() {
        return dataServerPort;
    }


    @Override
    public int getDataServerNotifyPort() {
        return dataServerNotifyPort;
    }


    @Override
    public int getHttpServerPort() {
        return httpServerPort;
    }


    @Override
    public int getAccessDataExecutorPoolSize() {
        return accessDataExecutorPoolSize;
    }


    @Override
    public int getAccessDataExecutorQueueSize() {
        return accessDataExecutorQueueSize;
    }


    @Override
    public int getAccessSubDataExecutorPoolSize() {
        return accessSubDataExecutorPoolSize;
    }

    @Override
    public int getAccessSubDataExecutorQueueSize() {
        return accessSubDataExecutorQueueSize;
    }


    @Override
    public int getDataChangeExecutorPoolSize() {
        return dataChangeExecutorPoolSize;
    }


    @Override
    public int getDataChangeExecutorQueueSize() {
        return dataChangeExecutorQueueSize;
    }


    @Override
    public int getDataChangeDebouncingMillis() {
        return dataChangeDebouncingMillis;
    }

    @Override
    public int getDataChangeMaxDebouncingMillis() {
        return dataChangeMaxDebouncingMillis;
    }

    @Override
    public int getPushTaskExecutorPoolSize() {
        return pushTaskExecutorPoolSize;
    }


    @Override
    public int getPushTaskExecutorQueueSize() {
        return pushTaskExecutorQueueSize;
    }


    @Override
    public int getPushDataTaskDebouncingMillis() {
        return pushDataTaskDebouncingMillis;
    }


    @Override
    public int getDataChangeFetchTaskWorkerSize() {
        return dataChangeFetchTaskWorkerSize;
    }


    @Override
    public int getPushDataTaskRetryFirstDelayMillis() {
        return pushDataTaskRetryFirstDelayMillis;
    }


    @Override
    public int getPushDataTaskRetryIncrementDelayMillis() {
        return pushDataTaskRetryIncrementDelayMillis;
    }


    @Override
    public int getDataClientConnNum() {
        return dataClientConnNum;
    }


    @Override
    public int getDataNotifyClientConnNum() {
        return dataNotifyClientConnNum;
    }

    @Override
    public boolean isInvalidForeverZone(String zoneId) {
        if (invalidForeverZonesSet == null) {
            String[] zoneNameArr = getInvalidForeverZones().split(";");
            Set<String> set = new HashSet<>();
            for (String str : zoneNameArr) {
                str = str.trim();
                if (str.length() != 0) {
                    set.add(str);
                }
            }
            invalidForeverZonesSet = set;
        }
        return invalidForeverZonesSet.contains(zoneId);
    }

    @Override
    public boolean isInvalidIgnored(String dataId) {
        if (invalidIgnoreDataIdPattern == null) {
            String invalidIgnoreDataidRegex = getInvalidIgnoreDataidRegex();
            if (StringUtils.isBlank(invalidIgnoreDataidRegex)) {
                invalidIgnoreDataIdPattern = Optional.empty();
            } else {
                invalidIgnoreDataIdPattern = Optional.of(Pattern.compile(invalidIgnoreDataidRegex));
            }
        }
        final Pattern pattern = invalidIgnoreDataIdPattern.get();
        return pattern != null && pattern.matcher(dataId).find();
    }


    @Override
    public int getSessionSchedulerPoolSize() {
        return sessionSchedulerPoolSize;
    }


    @Override
    public int getSlotSyncPublisherMaxNum() {
        return slotSyncPublisherMaxNum;
    }


    @Override
    public Collection<String> getMetaServerAddresses() {
        final Collection<String> addresses = metaAddresses;
        if (addresses != null) {
            return addresses;
        }
        metaAddresses = ServerEnv.getMetaAddresses(commonConfig.getMetaNode(), commonConfig.getDataCenter());
        return metaAddresses;
    }


    @Override
    public int getScanSubscriberIntervalMillis() {
        return scanSubscriberIntervalMillis;
    }


    @Override
    public int getCacheDigestIntervalMinutes() {
        return cacheDigestIntervalMinutes;
    }


    @Override
    public int getDataNodeExecutorWorkerSize() {
        return dataNodeExecutorWorkerSize;
    }

    @Override
    public int getDataNodeExecutorQueueSize() {
        return dataNodeExecutorQueueSize;
    }

    @Override
    public int getDataNodeRetryBackoffMillis() {
        return dataNodeRetryBackoffMillis;
    }

    @Override
    public int getDataNodeRetryTimes() {
        return dataNodeRetryTimes;
    }

    @Override
    public int getDataNodeRetryQueueSize() {
        return dataNodeRetryQueueSize;
    }

    @Override
    public int getDataNodeMaxBatchSize() {
        return dataNodeMaxBatchSize;
    }

    @Override
    public int getCacheCountIntervalSecs() {
        return cacheCountIntervalSecs;
    }

    @Override
    public int getCacheDatumMaxWeight() {
        return cacheDatumMaxWeight;
    }

    @Override
    public int getCacheDatumExpireSecs() {
        return cacheDatumExpireSecs;
    }

    @Override
    public int getSyncSessionIOLowWaterMark() {
        return syncSessionIOLowWaterMark;
    }

    @Override
    public int getSyncSessionIOHighWaterMark() {
        return syncSessionIOHighWaterMark;
    }


    @Override
    public int getClientManagerIntervalMillis() {
        return clientManagerIntervalMillis;
    }

    @Override
    public int getClientOpenIntervalSecs() {
        return clientOpenIntervalSecs;
    }


    @Override
    public int getAccessMetadataMaxBufferSize() {
        return accessMetadataMaxBufferSize;
    }

    @Override
    public int getAccessMetadataWorkerSize() {
        return accessMetadataWorkerSize;
    }

    @Override
    public int getMetaNodeBufferSize() {
        return metaNodeBufferSize;
    }

    @Override
    public int getMetaNodeWorkerSize() {
        return metaNodeWorkerSize;
    }

    @Override
    public int getClientIOLowWaterMark() {
        return clientIOLowWaterMark;
    }

    @Override
    public int getClientIOHighWaterMark() {
        return clientIOHighWaterMark;
    }

    @Override
    public int getSystemPropertyIntervalMillis() {
        return systemPropertyIntervalMillis;
    }

    @Override
    public int getClientManagerAddressIntervalMillis() {
        return clientManagerAddressIntervalMillis;
    }

    @Override
    public int getConsolePort() {
        return consolePort;
    }

    @Override
    public int getConsoleExecutorPoolSize() {
        return consoleExecutorPoolSize;
    }

    @Override
    public int getConsoleExecutorQueueSize() {
        return consoleExecutorQueueSize;
    }

    @Override
    public int getPushTaskBufferBucketSize() {
        return pushTaskBufferBucketSize;
    }


    @Override
    public int getPushCircuitBreakerThreshold() {
        return pushCircuitBreakerThreshold;
    }


    @Override
    public int getPushCircuitBreakerSilenceMillis() {
        return pushCircuitBreakerSilenceMillis;
    }

    @Override
    public int getClientNodePushConcurrencyLevel() {
        return clientNodePushConcurrencyLevel;
    }

    @Override
    public int getScanWatcherIntervalMillis() {
        return scanWatcherIntervalMillis;
    }

    @Override
    public boolean isGracefulShutdown() {
        return gracefulShutdown;
    }

    @Override
    public int getWatchConfigFetchBatchSize() {
        return watchConfigFetchBatchSize;
    }

    @Override
    public int getWatchConfigFetchIntervalMillis() {
        return watchConfigFetchIntervalMillis;
    }

    @Override
    public int getWatchConfigFetchLeaseSecs() {
        return watchConfigFetchLeaseSecs;
    }

    @Override
    public boolean isWatchConfigEnable() {
        return watchConfigEnable;
    }

    @Override
    public int getWatchPushTaskWorkerSize() {
        return watchPushTaskWorkerSize;
    }

    @Override
    public int getWatchPushTaskMaxBufferSize() {
        return watchPushTaskMaxBufferSize;
    }


    @Override
    public int getPushConsecutiveSuccess() {
        return pushConsecutiveSuccess;
    }


    @Override
    public int getPushAddressCircuitBreakerThreshold() {
        return pushAddressCircuitBreakerThreshold;
    }

    @Override
    public int getMetadataRegisterExecutorPoolSize() {
        return metadataRegisterExecutorPoolSize;
    }

    @Override
    public int getMetadataRegisterExecutorQueueSize() {
        return metadataRegisterExecutorQueueSize;
    }

    @Override
    public int getScanExecutorPoolSize() {
        return scanExecutorPoolSize;
    }

    @Override
    public int getScanExecutorQueueSize() {
        return scanExecutorQueueSize;
    }

    @Override
    public long getScanTimeoutMills() {
        return scanTimeoutMills;
    }

}
